package dao

import (
	"context"
	"gorm.io/gorm"
	"mall/api/mall"
	"mall/internal/model"
	"time"
)

func (d *Dao) FindAllShelfProdIds(ctx context.Context, sgId, pStoreId uint32) (
	prodIds []uint64, err error) {
	var prods []*model.ProdIdsQueryModel
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Select("prod_id").Where(
		"sg_id = ? AND p_store_id = ?",
		sgId, pStoreId).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	prodIds = make([]uint64, len(prods))
	for i, prod := range prods {
		prodIds[i] = prod.ProdID
	}
	return
}

func (d *Dao) FindShelfProdIdsByProdIds(ctx context.Context, sgId, pStoreId uint32, inputProdIds []uint64) (
	prodIds []uint64, err error) {
	var prods []*model.ProdIdsQueryModel
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Select("prod_id").Where(
		"sg_id = ? AND p_store_id = ? AND prod_id in ?",
		sgId, pStoreId, inputProdIds).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	prodIds = make([]uint64, len(prods))
	for i, prod := range prods {
		prodIds[i] = prod.ProdID
	}
	return
}

func (d *Dao) FindShelfProdIdsByPage(ctx context.Context, sgId, pStoreId, pageNo, pageSize uint32,
	inGroupId uint32, query string) (prodIds []uint64, total int64, err error) {
	var prods []*model.ProdIdsQueryModel
	db := d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"mall_shelf_prod.sg_id = ? AND mall_shelf_prod.p_store_id = ? AND shelf_status = ?",
		sgId, pStoreId, true)
	if inGroupId != 0 {
		db = db.Joins(
			"JOIN mall_shelf_group_ref ON mall_shelf_prod.prod_id=mall_shelf_group_ref.prod_id").Where(
			"group_id = ?", inGroupId)
	}
	if query != "" {
		query = "%" + query + "%"
		db = db.Where("custom_name like ? OR sync_name like ?", query, query)
	}
	db = db.Group("mall_shelf_prod.prod_id")
	if err = db.Count(&total).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	if err = db.Select("mall_shelf_prod.prod_id").Offset(int((pageNo - 1) * pageSize)).Limit(
		int(pageSize)).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	prodIds = make([]uint64, len(prods))
	for i, prod := range prods {
		prodIds[i] = prod.ProdID
	}
	return
}

func (d *Dao) FindShelfProdByCondition(ctx context.Context, sgId, pStoreId, pageNo, pageSize uint32,
	queryTypeId, inGroupId, excludedGroupId uint32, groupStatus mall.QueryGroupStatus, shelfStatus mall.QueryShelfStatus,
	query string) (
	total int64, prods []*model.ShelfProd, err error) {
	db := d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"mall_shelf_prod.sg_id = ? AND mall_shelf_prod.p_store_id = ?", sgId, pStoreId)
	if queryTypeId != 0 {
		db = db.Where("sync_type_id = ?", queryTypeId)
	}
	if inGroupId != 0 || excludedGroupId != 0 || groupStatus != mall.QueryGroupStatus_GROUP_ALL {
		db = db.Joins(
			"LEFT OUTER JOIN mall_shelf_group_ref ON mall_shelf_prod.prod_id = mall_shelf_group_ref.prod_id")
		if inGroupId != 0 {
			db = db.Where("group_id = ?", inGroupId)
		}
		if excludedGroupId != 0 {
			db = db.Where("group_id != ?", excludedGroupId)
		}
		switch groupStatus {
		case mall.QueryGroupStatus_GROUP_OFF:
			db = db.Where("group_id is NULL")
		case mall.QueryGroupStatus_GROUP_ON:
			db = db.Where("group_id is not NULL")
		}
	}
	switch shelfStatus {
	case mall.QueryShelfStatus_SHELF_ON:
		db = db.Where("shelf_status = ?", true)
	case mall.QueryShelfStatus_SHELF_OFF:
		db = db.Where("shelf_status = ?", false)
	}
	if query != "" {
		query = "%" + query + "%"
		db = db.Where("custom_name like ? OR sync_name like ?", query, query)
	}
	db = db.Group("mall_shelf_prod.prod_id")
	if err = db.Count(&total).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	var prodIdList []*model.ProdIdsQueryModel
	if err = db.Select("mall_shelf_prod.prod_id").Order("mall_shelf_prod.prod_id asc").Offset(int((pageNo - 1) * pageSize)).Limit(
		int(pageSize)).Find(&prodIdList).Error; err != nil {
		err = d.processSqlError(err)
	}
	if len(prodIdList) != 0 {
		prodIds := make([]uint64, len(prodIdList))
		for i, p := range prodIdList {
			prodIds[i] = p.ProdID
		}
		if prods, err = d.FindShelfProdsByRawProdIds(ctx, sgId, prodIds); err != nil {
			return
		}
	} else {
		prods = make([]*model.ShelfProd, 0)
	}
	return
}

func (d *Dao) FindShelfProdsByProdIds(ctx context.Context, sgId, pStoreId uint32, prodIds []uint64) (
	prods []*model.ShelfProd, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"sg_id = ? AND p_store_id = ? AND prod_id in ?",
		sgId, pStoreId, prodIds).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfProdsByRawProdIds(ctx context.Context, sgId uint32, prodIds []uint64) (
	prods []*model.ShelfProd, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"sg_id = ? AND prod_id in ?",
		sgId, prodIds).Order("prod_id asc").Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOnShelfProdsByProdIds(ctx context.Context, sgId, pStoreId uint32, prodIds []uint64) (
	prods []*model.ShelfProd, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"prod_id in ? AND p_store_id = ? AND sg_id = ? AND shelf_status = ?",
		prodIds, pStoreId, sgId, true).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOnShelfProdsByProdIdsAndStoreIds(
	ctx context.Context, sgId uint32, pStoreIds []uint32, prodIds []uint64) (
	prods []*model.ShelfProd, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"prod_id in ? AND p_store_id in ? AND sg_id = ? AND shelf_status = ?",
		prodIds, pStoreIds, sgId, true).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindOnShelfSkusBySkuIdsAndStoreIds(
	ctx context.Context, sgId uint32, pStoreIds []uint32, skuIds []uint64) (
	skus []*model.ShelfSku, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"sku_id in ? AND p_store_id in ? AND sg_id = ?",
		skuIds, pStoreIds, sgId, true).Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfProdsByProdIdsWithoutPStoreId(ctx context.Context, sgId uint32, inputProdIds []uint64) (
	prodIds []uint64, err error) {
	var prods []*model.ProdIdsQueryModel
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Select("prod_id").Where(
		"sg_id = ? AND prod_id in ?",
		sgId, inputProdIds).Scan(&prods).Error; err != nil {
		err = d.processSqlError(err)
	}
	prodIds = make([]uint64, len(prods))
	for i, prod := range prods {
		prodIds[i] = prod.ProdID
	}
	return
}

func (d *Dao) FindShelfSkusByProdIds(ctx context.Context, sgId, pStoreId uint32, prodIds []uint64) (
	skus []*model.ShelfSku, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"prod_id in ? AND p_store_id = ? AND sg_id = ?",
		prodIds, pStoreId, sgId).Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfSkusByRawProdIds(ctx context.Context, sgId uint32, prodIds []uint64) (
	skus []*model.ShelfSku, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"prod_id in ? AND sg_id = ?",
		prodIds, sgId).Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfSkusBySkuIds(ctx context.Context, pStoreId uint32, skuIds []uint64) (
	skus []*model.ShelfSku, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"sku_id in ? AND p_store_id = ?",
		skuIds, pStoreId).Scan(&skus).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetShelfProd(ctx context.Context, prodId uint64, sgId, pStoreId uint32) (
	shelfProd *model.ShelfProd, err error) {
	shelfProd = &model.ShelfProd{}
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"prod_id = ? AND p_store_id = ? AND sg_id = ?",
		prodId, pStoreId, sgId).Take(shelfProd).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetShelfSku(ctx context.Context, skuId uint64, sgId, pStoreId uint32) (
	shelfSku *model.ShelfSku, err error) {
	shelfSku = &model.ShelfSku{}
	if err = d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"sku_id = ? AND p_store_id = ? AND sg_id = ?",
		skuId, pStoreId, sgId).Take(shelfSku).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) GetShelfGroup(ctx context.Context, groupId, sgId, pStoreId uint32) (
	group *model.ShelfGroup, err error) {
	group = &model.ShelfGroup{}
	if err = d.TransContext(ctx).Model(&model.ShelfGroup{}).Where(
		"id = ? AND sg_id = ? AND p_store_id = ? and status = ?",
		groupId, sgId, pStoreId, true).Take(group).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) CheckShelfGroupNameDup(ctx context.Context, name string, sgId, pStoreId uint32) (
	existed bool, err error) {
	var groups []*model.ShelfGroup
	if err = d.TransContext(ctx).Model(&model.ShelfGroup{}).Where(
		"sg_id = ? AND p_store_id = ? AND name = ? AND status = ?",
		sgId, pStoreId, name, true).Scan(&groups).Error; err != nil {
		err = d.processSqlError(err)
	}
	existed = len(groups) != 0
	return
}

func (d *Dao) FindShelfGroupsBySgId(ctx context.Context, sgId, pStoreId uint32) (
	groups []*model.ShelfGroup, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfGroup{}).Where(
		"sg_id = ? AND p_store_id = ? and status = ?",
		sgId, pStoreId, true).Order("code asc").Scan(&groups).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfGroupsByGroupIds(ctx context.Context, ids []uint32, sgId, pStoreId uint32) (
	groups []*model.ShelfGroup, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfGroup{}).Where(
		"id in ? AND sg_id = ? AND p_store_id = ? and status = ?",
		ids, sgId, pStoreId, true).Scan(&groups).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfGroupRefByProdIdsAndGroupIds(ctx context.Context, prodIds []uint64,
	groupIds []uint32) (refs []*model.ShelfGroupRef, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfGroupRef{}).Where(
		"group_id in ? AND prod_id in ?",
		groupIds, prodIds).Scan(&refs).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindShelfGroupRefByProdIds(ctx context.Context, sgId, pStoreId uint32, prodIds []uint64) (
	refs []*model.ShelfGroupRef, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfGroupRef{}).Where(
		"prod_id in ? AND p_store_id = ? AND sg_id = ?",
		prodIds, pStoreId, sgId).Scan(&refs).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) FindGroupViewsByProdIds(ctx context.Context, pStoreId uint32, prodIds []uint64) (
	groups []*model.GroupRefQueryModel, err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfGroupRef{}).Joins(
		"JOIN mall_shelf_group ON group_id = id").Select(
		"prod_id, name, id as group_id").Where(
		"prod_id in ? AND mall_shelf_group_ref.p_store_id = ? AND status = ?",
		prodIds, pStoreId, true).Scan(&groups).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) BatchUpdateShelfStatus(ctx context.Context, pStoreId uint32, prodIds []uint64,
	status bool, operator uint32) (err error) {
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Where(
		"prod_id in ? and p_store_id = ?",
		prodIds, pStoreId).Updates(map[string]interface{}{
		"shelf_status": status,
		"operator":     operator,
	}).Error; err != nil {
		err = d.processSqlError(err)
	}
	return
}

func (d *Dao) DeleteShelfGroupRefByProdIds(ctx context.Context, groupId uint32, prodIds []uint64) (err error) {
	if err = d.TransContext(ctx).Delete(&model.ShelfGroupRef{},
		"group_id = ? and prod_id in ?",
		groupId, prodIds).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) DeleteShelfGroupRefByGroupIds(ctx context.Context, groupIds []uint32, prodId uint64) (err error) {
	if err = d.TransContext(ctx).Delete(&model.ShelfGroupRef{},
		"group_id in ? and prod_id = ?",
		groupIds, prodId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) DeleteShelfProds(ctx context.Context, pStoreId uint32, prodIds []uint64) (err error) {
	if err = d.TransContext(ctx).Delete(&model.ShelfSku{},
		"prod_id in ? and p_store_id = ?",
		prodIds, pStoreId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	if err = d.TransContext(ctx).Delete(&model.ShelfGroupRef{},
		"prod_id in ? and p_store_id = ?",
		prodIds, pStoreId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	if err = d.TransContext(ctx).Delete(&model.ShelfProd{},
		"prod_id in ? and p_store_id = ?",
		prodIds, pStoreId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) DeleteShelfProdsByProdIds(ctx context.Context, prodIds []uint64, sgId uint32) (err error) {
	if err = d.TransContext(ctx).Delete(&model.ShelfSku{},
		"prod_id in ? AND sg_id = ?",
		prodIds, sgId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	if err = d.TransContext(ctx).Delete(&model.ShelfGroupRef{},
		"prod_id in ? AND sg_id = ?",
		prodIds, sgId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	if err = d.TransContext(ctx).Delete(&model.ShelfProd{},
		"prod_id in ? AND sg_id = ?",
		prodIds, sgId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) DeleteShelfSku(ctx context.Context, skuId uint64, pStoreId uint32) (err error) {
	if err = d.TransContext(ctx).Delete(&model.ShelfSku{},
		"sku_id = ? AND p_store_id = ?",
		skuId, pStoreId).Error; err != nil {
		err = d.processSqlError(err)
		return
	}
	return
}

func (d *Dao) FindShelfProdTypeIds(ctx context.Context, sgId, pStoreId uint32) (typeIds []uint32, err error) {
	var types []*model.ProdTypeQueryModel
	if err = d.TransContext(ctx).Model(&model.ShelfProd{}).Select("sync_type_id").Where(
		"sg_id = ? AND p_store_id = ?",
		sgId, pStoreId).Group("sync_type_id").Scan(&types).Error; err != nil {
		err = d.processSqlError(err)
	}
	typeIds = make([]uint32, len(types))
	for i, prodType := range types {
		typeIds[i] = prodType.ProdTypeID
	}
	return
}

func (d *Dao) ResetSkuPrice(ctx context.Context, sgId uint32, storeIds []uint32, syncTime time.Time) (err error) {
	err = d.processSqlError(d.TransContext(ctx).Model(&model.ShelfSku{}).Where(
		"sg_id = ? AND p_store_id in ? AND sync_time < ?",
		sgId, storeIds, syncTime).Updates(map[string]interface{}{
		"is_customized": false,
		"custom_price":  gorm.Expr("mall_shelf_sku.sync_price"),
		"sync_time":     syncTime,
	}).Error)
	return
}
